package vip.ola.pay.ctrl.payment;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import vip.ola.core.common.constant.MchConstant;
import vip.ola.core.common.constant.PayConstant;
import vip.ola.core.common.constant.PayEnum;
import vip.ola.core.common.util.*;
import vip.ola.core.common.vo.OrderCostFeeVO;
import vip.ola.core.entity.*;
import vip.ola.pay.channel.PaymentInterface;
import vip.ola.pay.ctrl.common.BaseController;
import vip.ola.pay.service.PayOrderService;
import vip.ola.pay.service.RpcCommonService;
import vip.ola.pay.util.SpringUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;

import static vip.ola.core.common.constant.PayConstant.*;
import static vip.ola.core.common.constant.PayEnum.ERR_0010;

/**
 * @author dingzhiwei jmdhappy@126.com
 * @version V1.0
 * @Description: 支付订单, 包括:统一下单,订单查询,补单等接口
 * @date 2017-07-05
 */
@Controller
public class PayOrderController extends BaseController {

    private final MyLog _log = MyLog.getLog(PayOrderController.class);

    @Autowired
    private RpcCommonService rpcCommonService;

    @Autowired
    private PayOrderService payOrderService;

    /**
     * 统一下单接口:
     * 1)先验证接口参数以及签名信息
     * 2)验证通过创建支付订单
     * 3)根据商户选择渠道,调用支付服务进行下单
     * 4)返回下单数据
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "/apis/pay/createde")
    @ResponseBody
    public String payOrder(HttpServletRequest request, HttpServletResponse response) {
        _log.info("###### 开始接收商户统一下单请求 ######");
        String logPrefix = "【商户统一下单】";
        try {
            JSONObject po = getJsonParam(request);
            _log.info("{}请求参数:{}", logPrefix, po);
            JSONObject payContext = new JSONObject();
            PayOrder payOrder = null;
            // 验证参数有效性
            Object object = validateParams(po, payContext, request);
            if (object instanceof String) {
                _log.info("{}参数校验不通过:{}", logPrefix, object);
                return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL, object.toString(), null, PayEnum.ERR_0014.getCode(), object.toString()));
            }
            if (object instanceof PayOrder) {
                payOrder = (PayOrder) object;
            }
            if (payOrder == null) {
                return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL, "支付中心下单失败", null, ERR_0010.getCode(), "生成支付订单失败"));
            }
            String channelType = payOrder.getChannelType();
            // 默认调用码商接口
            PaymentInterface paymentInterface;
            try {
                paymentInterface = (PaymentInterface) SpringUtil.getBean(channelType.toLowerCase() + "PaymentService");
            } catch (Exception e) {
                _log.error(e, "支付渠道类型[channelType=" + channelType + "]实例化异常");
                return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL, "调用支付渠道失败", null, ERR_0010.getCode(), "支付渠道类型[channelType=" + channelType + "]实例化异常"));
            }
            // 如果该通道重新定义了订单号,那么使用新的订单号
            String orderId = paymentInterface.getOrderId(payOrder);
            if (StringUtils.isNotBlank(orderId)) {
                payOrder.setPayOrderId(orderId);
            }
            // 如果该通道重新设置订单金额，那么重写订单金额及分润
            Long newAmount = paymentInterface.getAmount(payOrder);
            if (newAmount != null) { // 通道实现了getAmount方法
                if (newAmount == -1) {   // 表示当前金额不可用，需更换金额重新下单
                    return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL, "支付中心下单失败", null, ERR_0010.getCode(), "请更换金额重新下单"));
                } else if (newAmount > 0) {
                    payOrder.setAmount(newAmount);
                    // 重新计算订单:渠道成本费用,代理商费用,商户入账,平台利润
                    OrderCostFeeVO orderCostFeeVO = PayUtil.calOrderCostFeeAndIncome(newAmount, payOrder.getChannelRate(), payOrder.getAgentRate(), payOrder.getParentAgentRate(), payOrder.getMchRate());
                    // 重新设置渠道成本及分润
                    payOrder.setChannelCost(orderCostFeeVO.getChannelCostFee());
                    payOrder.setPlatProfit(orderCostFeeVO.getPlatProfit());
                    payOrder.setAgentProfit(orderCostFeeVO.getAgentProfit());
                    payOrder.setParentAgentProfit(orderCostFeeVO.getParentAgentProfit());
                    payOrder.setMchIncome(orderCostFeeVO.getMchIncome());
                }
            }
            int result = rpcCommonService.rpcPayOrderService.createPayOrder(payOrder);
            _log.info("{}创建支付订单,结果:{}", logPrefix, result);
            if (result != 1) {
                return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL, "支付中心下单失败", null, ERR_0010.getCode(), "DB插入支付订单失败"));
            }
            // 执行支付
            JSONObject retObj = paymentInterface.pay(payOrder);
            if (retObj.get(RETURN_PARAM_RETCODE).equals(RETURN_VALUE_SUCCESS)) {
                retObj.put("payOrderId", payOrder.getPayOrderId());
                // 使用StringEscapeUtils.unescapeJava去掉字符串中的转义符号(不采用,会导致json解析报错)
                //return StringEscapeUtils.unescapeJava(PayUtil.makeRetData(retObj, payContext.getString("key")));
                return PayUtil.makeRetData(retObj, payContext.getString("key"));
            } else {
                return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL,
                        "调用支付渠道失败" + (retObj.get(RETURN_PARAM_RETMSG) == null ? "" : ("(" + retObj.get(RETURN_PARAM_RETMSG) + ")")),
                        null, retObj.getString("errCode"), retObj.getString("errDes")));
            }
        } catch (Exception e) {
            _log.error(e, "支付中心系统异常.");
            return PayUtil.makeRetFail(PayUtil.makeRetMap(RETURN_VALUE_FAIL, "支付中心系统异常", null, ERR_0010.getCode(), "请联系技术人员查看"));
        }
    }

    /**
     * 服务端跳转
     *
     * @param request
     * @return
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("/api/jump.htm")
    public String toPay(HttpServletRequest request, ModelMap model) throws ServletException, IOException {
        String params = request.getParameter("params");
        if (StringUtils.isNotBlank(params)) {
            String jumpForm = new String(MyBase64.decode(params));
            model.put("jumpForm", jumpForm);
        } else {
            model.put("jumpForm", "跳转出现异常,请联系管理员.");
        }
        return "payment/jump";
    }

    /**
     * 验证创建订单请求参数,参数通过返回JSONObject对象,否则返回错误文本信息
     *
     * @param params
     * @return
     */
    private Object validateParams(JSONObject params, JSONObject payContext, HttpServletRequest request) {
        String riskLog = "[支付风控]";
        // 验证请求参数,参数有问题返回错误提示
        String errorMessage;
        // 支付参数
        String mchId = params.getString("mch_id");              // 商户ID
        String productId = params.getString("product_id");      // 支付产品ID
        String mchOrderNo = params.getString("out_trade_no");    // 商户订单号
        String amount = params.getString("amount");            // 支付金额（单位分）
        String extra = params.getString("extra");              // 特定渠道发起时额外参数
        String param1 = params.getString("param1");            // 扩展参数1
        String param2 = params.getString("param2");            // 扩展参数2
        String returnUrl = params.getString("return_url");      // 支付结果同步请求url
        String notifyUrl = params.getString("notify_url");      // 支付结果回调URL
        String sign = params.getString("sign");                // 签名
        String subject = params.getString("subject");          // 商品主题
        String body = params.getString("body");                // 商品描述信息

        // 验证请求参数有效性（必选项）
        Long mchIdL;
        if (StringUtils.isBlank(mchId) || !NumberUtils.isDigits(mchId)) {
            errorMessage = "请求参数[mchId]不能为空且为数值类型.";
            return errorMessage;
        }
        mchIdL = Long.parseLong(mchId);
        // 查询商户信息
        MchInfo mchInfo = rpcCommonService.rpcMchInfoService.findByMchId(mchIdL);
        if (mchInfo == null) {
            errorMessage = "商户不存在[mchId=" + mchId + "].";
            return errorMessage;
        }
        if (mchInfo.getStatus() != MchConstant.PUB_YES) {
            errorMessage = "商户状态不可用[mchId=" + mchId + "].";
            return errorMessage;
        }
        // 判断请求IP是否允许
        String clintIp = IPUtility.getClientIp(request);
        boolean isAllow = PayUtil.ipAllow(clintIp, mchInfo.getPayWhiteIp(), mchInfo.getPayBlackIp());
        if (!isAllow) {
            errorMessage = "IP[" + clintIp + "]不允许访问";
            return errorMessage;
        }

        Integer productIdI;
        if (StringUtils.isBlank(productId) || !NumberUtils.isDigits(productId)) {
            errorMessage = "请求参数[productId]不能为空且为数值类型.";
            return errorMessage;
        }
        productIdI = Integer.parseInt(productId);

        if (StringUtils.isBlank(mchOrderNo)) {
            errorMessage = "请求参数[mchOrderNo]不能为空.";
            return errorMessage;
        }

        if (!NumberUtils.isDigits(amount)) {
            errorMessage = "请求参数[amount]应为数值类型.";
            return errorMessage;
        }
        Long amountL = Long.parseLong(amount);
        if (amountL <= 0) {
            errorMessage = "请求参数[amount]必须大于0.";
            return errorMessage;
        }
        if (StringUtils.isBlank(notifyUrl)) {
            errorMessage = "请求参数[notifyUrl]不能为空.";
            return errorMessage;
        }
        if (StringUtils.isBlank(subject)) {
            errorMessage = "请求参数[subject]不能为空.";
            return errorMessage;
        }
        if (StringUtils.isBlank(body)) {
            errorMessage = "请求参数[body]不能为空.";
            return errorMessage;
        }
        String clientIp = IPUtility.getClientIp(request);
        String channelUser = "";

        // 签名信息
        if (StringUtils.isEmpty(sign)) {
            errorMessage = "请求参数[sign]不能为空.";
            return errorMessage;
        }

        String key = mchInfo.getPrivateKey();
        if (StringUtils.isBlank(key)) {
            errorMessage = "商户私钥为空,请配置商户私钥[mchId=" + mchId + "].";
            return errorMessage;
        }
        payContext.put("key", key);

        // 查询商户对应的支付渠道
        String channelMchId;
        String channelType;
        String channelId;
        BigDecimal channelRate;
        BigDecimal mchRate;
        BigDecimal parentAgentRate = null;
        BigDecimal agentRate = null;
        Integer passageAccountId;

        MchPayPassage mchPayPassage = rpcCommonService.rpcMchPayPassageService.findByMchIdAndProductId(mchIdL, productIdI);

        if (mchPayPassage == null) {
            errorMessage = "商户没有该产品的支付通道[productId=" + productId + ",mchId=" + mchId + "]";
            return errorMessage;
        }
        if (mchPayPassage.getStatus() != MchConstant.PUB_YES) {
            errorMessage = "商户该产品的支付通道[productId=" + productId + ",mchId=" + mchId + "]已关闭";
            return errorMessage;
        }

        // 支付通道ID
        Integer payPassageId = null;
        PayPassageAccount payPassageAccount = null;
        Integer payPassAccountId = mchPayPassage.getPayPassageAccountId();
        if (payPassAccountId != null && payPassAccountId > 0) {
            payPassageAccount = rpcCommonService.rpcPayPassageAccountService.findById(payPassAccountId);
            if (payPassageAccount == null || payPassageAccount.getStatus() != MchConstant.PUB_YES) {
                errorMessage = "通道子账户不可用[payPassAccountId=" + payPassAccountId + ",mchId=" + mchId + "]";
                return errorMessage;
            }
            payPassageId = payPassageAccount.getPayPassageId();
        } else {
            // 获取通道子账户
            Object obj = payOrderService.getPayPassageAccount(mchPayPassage, riskLog, amountL);
            if (obj instanceof String) {
                return obj;
            }
            if (obj instanceof PayPassageAccount) {
                payPassageAccount = (PayPassageAccount) obj;
                payPassageId = payPassageAccount.getPayPassageId();
            }
        }
        // 判断支付通道
        if (payPassageId == null) {
            errorMessage = "无法取得可用的支付通道[productId=" + productId + ",mchId=" + mchId + "]";
            return errorMessage;
        }
        // 判断子账户
        if (payPassageAccount == null) {
            errorMessage = "该支付通道没有可用子账户[payPassageId=" + payPassageId + "]";
            return errorMessage;
        }
        passageAccountId = payPassageAccount.getId();
        channelMchId = payPassageAccount.getPassageMchId();
        channelType = payPassageAccount.getIfTypeCode();
        channelId = payPassageAccount.getIfCode();
        channelRate = payPassageAccount.getPassageRate();
        mchRate = mchPayPassage.getMchRate();

        // 处理二级代理商
        Long agentId = mchInfo.getAgentId();
        Long parentAgentId = mchInfo.getParentAgentId();
        if (agentId != null) {
            AgentPassage agentPassage = rpcCommonService.rpcAgentPassageService.findByAgentIdAndProductId(agentId, productIdI);
            if (agentPassage != null && agentPassage.getStatus() == MchConstant.PUB_YES) {
                agentRate = agentPassage.getAgentRate();
                if (agentRate == null) {
                    errorMessage = "请设置二级代理商费率";
                    return errorMessage;
                }
            } else {
                errorMessage = "请设置代理商费率";
                return errorMessage;
            }
            //处理一级代理商
            if (parentAgentId != null && parentAgentId != 0) {
                AgentPassage agentPassage2 = rpcCommonService.rpcAgentPassageService.findByAgentIdAndProductId(parentAgentId, productIdI);
                if (agentPassage2 != null && agentPassage2.getStatus() == MchConstant.PUB_YES) {
                    parentAgentRate = agentPassage2.getAgentRate();
                }
                if (parentAgentRate == null) {
                    errorMessage = "请设置一级代理商费率";
                    return errorMessage;
                }
            }
        }

        if (channelType == null || channelId == null) {
            errorMessage = "商户没有该产品的支付通道[productId=" + productId + ",mchId=" + mchId + ",channelType=" + channelType + ",channelId=" + channelId + "]";
            return errorMessage;
        }

        // 根据不同渠道,判断extra参数
        if (PayConstant.PAY_CHANNEL_WX_JSAPI.equalsIgnoreCase(channelId)) {
            if (StringUtils.isEmpty(extra)) {
                errorMessage = "request params[extra] error.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String openId = extraObject.getString("openId");
            if (StringUtils.isBlank(openId)) {
                errorMessage = "request params[extra.openId] error.";
                return errorMessage;
            }
            channelUser = openId;
        } else if (PayConstant.PAY_CHANNEL_WX_MWEB.equalsIgnoreCase(channelId)) {
            if (StringUtils.isEmpty(extra)) {
                errorMessage = "请求参数[extra]不能为空.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String sceneInfo = extraObject.getString("sceneInfo");
            if (StringUtils.isBlank(sceneInfo)) {
                errorMessage = "请求参数[extra.sceneInfo]不能为空.";
                return errorMessage;
            }
            if (StringUtils.isBlank(clientIp)) {
                errorMessage = "请求参数[clientIp]不能为空.";
                return errorMessage;
            }
        } else if (PayConstant.PAY_CHANNEL_ACCOUNTPAY_BALANCE.equalsIgnoreCase(channelId)) {
            if (StringUtils.isEmpty(extra)) {
                errorMessage = "请求参数[extra]不能为空.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String userId = extraObject.getString("userId");
            if (StringUtils.isBlank(userId)) {
                errorMessage = "请求参数[extra.userId]不能为空.";
                return errorMessage;
            }
            channelUser = userId;
        }

        // 验证签名数据
        boolean verifyFlag = PayUtil.verifyPaySign(params, key);
        if (!verifyFlag) {
            errorMessage = "验证签名失败.";
            return errorMessage;
        }

        // 验证参数通过,返回JSONObject对象
        PayOrder payOrder = new PayOrder();
//        if (channelType.equals("abc")) {
//            payOrder.setPayOrderId(mchOrderNo);
//        } else {
//            payOrder.setPayOrderId(MySeq.getPay());
//        }
        payOrder.setPayOrderId(MySeq.getPay());
        payOrder.setMchId(mchIdL);
        payOrder.setMchName(mchInfo.getName());
        payOrder.setMchType(mchInfo.getType());
        payOrder.setMchOrderNo(mchOrderNo);
        payOrder.setAgentId(agentId);
        payOrder.setParentAgentId(parentAgentId);
        payOrder.setProductName(mchPayPassage.getProductName());
        payOrder.setProductId(productIdI);                          // 支付产品ID
        payOrder.setProductType(mchPayPassage.getProductType());    // 产品类型
        payOrder.setPassageId(payPassageId);                        // 支付通道ID
        payOrder.setPassageAccountId(passageAccountId);             // 支付通道账户ID
        payOrder.setChannelType(channelType);
        payOrder.setChannelId(channelId);
        payOrder.setAmount(amountL);
        payOrder.setClientIp(clientIp);
        payOrder.setSubject(subject);
        payOrder.setBody(body);
        payOrder.setExtra(extra);
        payOrder.setChannelMchId(channelMchId);
        payOrder.setChannelUser(channelUser);
        // 设置费率
        payOrder.setChannelRate(channelRate);
        payOrder.setAgentRate(agentRate);
        payOrder.setParentAgentRate(parentAgentRate);
        payOrder.setMchRate(mchRate);
        // 计算订单:渠道成本费用,代理商费用,商户入账,平台利润
        OrderCostFeeVO orderCostFeeVO = PayUtil.calOrderCostFeeAndIncome(amountL, channelRate, agentRate, parentAgentRate, mchRate);
        // 设置渠道成本及分润
        payOrder.setChannelCost(orderCostFeeVO.getChannelCostFee());
        payOrder.setPlatProfit(orderCostFeeVO.getPlatProfit());
        payOrder.setAgentProfit(orderCostFeeVO.getAgentProfit());
        payOrder.setParentAgentProfit(orderCostFeeVO.getParentAgentProfit());
        payOrder.setMchIncome(orderCostFeeVO.getMchIncome());

        payOrder.setParam1(param1);
        payOrder.setParam2(param2);
        payOrder.setNotifyUrl(notifyUrl);
        payOrder.setReturnUrl(returnUrl);
        return payOrder;
    }

}
